import 'dart:async';

import 'package:rxdart/rxdart.dart';

import '../../common/test_page.dart';
import '../utils.dart';

class DistinctTestPage extends TestPage {
  DistinctTestPage(super.title) {
    test('Rx.distinct', () async {
      const expected = 1;

      final stream = Stream.fromIterable(const [expected, expected]).distinct();

      stream.listen(((actual) {
        expect(actual);
      }));
    });
    test('Rx.distinct accidental broadcast', () async {
      final controller = StreamController<int>();

      final stream = controller.stream.distinct();

      stream.listen(null);
      expect(() => stream.listen(null));

      controller.add(1);
    });

    group('DistinctUniqueStreamTransformer', () {
      test('works with the equals and hascode of the class', () async {
        final stream = Stream.fromIterable(const [
          _TestObject('a'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('c'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('c'),
          _TestObject('a')
        ]).distinctUnique();

        expect(
            stream,
        );
      });

      test('works with a provided equals and hashcode', () async {
        final stream = Stream.fromIterable(const [
          _TestObject('a'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('c'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('c'),
          _TestObject('a')
        ]).distinctUnique(
            equals: (a, b) => a.key == b.key, hashCode: (o) => o.key.hashCode);

        expect(
            stream,
        );
      });

      test(
          'sends an error to the subscription if an error occurs in the equals or hashmap methods',
              () async {
            final stream = Stream.fromIterable(
                const [_TestObject('a'), _TestObject('b'), _TestObject('c')])
                .distinctUnique(
                equals: (a, b) => a.key == b.key,
                hashCode: (o) => throw Exception('Catch me if you can!'));

            stream.listen(
              null,
              onError: (Object e, StackTrace s) => expect(e));
          });

      test('is reusable', () async {
        const data = [
          _TestObject('a'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('a'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('b'),
          _TestObject('c'),
          _TestObject('a'),
          _TestObject('b'),
          _TestObject('c'),
          _TestObject('a')
        ];

        final distinctUniqueStreamTransformer =
        DistinctUniqueStreamTransformer<_TestObject>();

        final firstStream =
        Stream.fromIterable(data).transform(distinctUniqueStreamTransformer);

        final secondStream =
        Stream.fromIterable(data).transform(distinctUniqueStreamTransformer);

        expect(
            firstStream,
        );

        expect(
            secondStream,
        );
      });

      test('Rx.distinctUnique accidental broadcast', () async {
        final controller = StreamController<int>();

        final stream = controller.stream.distinctUnique();

        stream.listen(null);
        expect(() => stream.listen(null));

        controller.add(1);
      });
    });

    test('Rx.distinctUnique.nullable', () {
      nullableTest<String?>(
            (s) => s.distinctUnique(),
      );
    });

  }

}

class _TestObject {
  final String key;

  const _TestObject(this.key);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is _TestObject &&
              runtimeType == other.runtimeType &&
              key == other.key;

  @override
  int get hashCode => key.hashCode;

  @override
  String toString() => key;
}